<?php
namespace Tbf\Crypto;
use Tbf\Util\CryptoRandUtil;
/**
 * 加密接口adapter
 * 注意：
 * 所有公开接口输入数据均不限长度，2进制安全。 
 * 所有返回数据均为2进制。使用bin2hex函数转为普通数据。
 * 不同配置，不兼容
 * 安全性和密码长度和加密方法有关。
 * 使用use PKCS5/7 style padding openssl默认
 *
 * @deprecated
 *
 */
class Crypto{
    //对外接口部分
    /////////////////////////////////////////
    /**
    * hash密码
    @param string $data 输入密码
    @param string $salt 盐
    @param string $key 站点密码
    */
    static function hash($data,$salt='',$key=null){
        $obj = self::loadCrypto('hash');
        return $obj->_hash($data,$salt,$key);
    }
    /**
     * 检测密码是否匹配
      @param string $input 密码原文
      @param string $store 储存密码 由generatePassword生成
      @param string $key 站点密码
     */
    static function checkPassword($input,$store,$key=''){
        $obj = self::loadCrypto('hash');
        return $obj->_checkPassword($input,$store,$key);   
    }
    /**
     * 生成储存密码
      @param string $input 密码原文
      @param string $key 站点密码
     */
    static function generatePassword($input,$key=''){
        $obj = self::loadCrypto('hash');
        return $obj->_generatePassword($input,$key);               
    }
    /**
     * hash数据到需要的长度
     @param string $data 数据
     @param string $length 长度
     */
    static function hashToLength($data,$length){
        $obj = self::loadCrypto('hash');
        return $obj->_hashToLength($data,$length);
    }
    /**
     * 对称加密的加密
     @param string $data 数据
     @param string $key 密码
     */
    static function cipherEn($data,$key=''){
        $obj = self::loadCrypto('cipher');
        return $obj->_cipherEn($data, $key);
    }
    /**
     * 对称加密的解密
     @param string $data 数据
     @param string $key 密码
     */
    static function cipherDe($data,$key=null){
        return self::loadCrypto('cipher')->_cipherDe($data,$key);
    }
    /**
     * 不对称加密的加密（未实现）
     @param string $data 数据
     @param string $key 密码
     */
    static function pkeyEn($data,$key=null){
        return self::dObj()->_pkeyEn($data,$key);
    }
    /**
     * 不对称加密的解密（未实现）
     @param string $data 数据
     @param string $key 密码
     */
    static function pkeyDe($data,$key=null){
        return self::dObj()->_pkeyDe($data,$key);
    }
    /**
     * 16进制字符串转二进制-工具函数
     * */
    static function hex2bin($hex){
        return pack('H*',$hex);
    }

    //内部接口转换
    /////////////////////////////////////////
    /**
     * 检测密码是否匹配
      @param string $input 密码原文
      @param string $store 储存密码 由generatePassword生成
      @param string $key 站点密码
     */
    function _checkPassword($input,$store,$key){
        $salt = substr($store,0,$this->config['hash'][2]);
        $hashed = substr($store,$this->config['hash'][2]);
        $output = $this->hash($input,$salt,$key);
        if ($output===$hashed){
            return true;
        }else{
            return false;
        }
    }
    /**
     * 生成储存密码(内部)
      @param string $input 密码原文
      @param string $key 站点密码
     */
    function _generatePassword($input,$key){
        $salt = CryptoRandUtil::binary($this->config['hash'][2]);
        $output = $this->hash($input,$salt,$key);
        $output = $salt.$output;
        return $output;     
    }
    /**
    * hash数据到需要的长度(内部)
    @param string $data 数据
    @param string $length 长度
    */
    function _hashToLength($data,$length){
        $hashType = $this->config['hash'][0];
        $data = $this->dHash($data,$hashType);
        while (true){
            if (strlen($data)>=$length){
                $data = substr($data,0,$length);
                break;
            }elseif (strlen($data)<$length){
                $data.=$this->dHash($data,$hashType);
            }
        }
        return $data;
    }
    /**
     * 对称加密获得加密用密码(内部)
     @param string $key 输入密码
     */
    function _cipherGetKey($key){
        $cipherType = $this->config['cipher'][0];
        $mode =  $this->config['cipher'][1];
        $keyLen = $this->cipherKeySize($cipherType);
        $ivLen = $this->cipherIvSize($cipherType);
        $key = $key.$this->config['key'];
        if (strlen($key)!=$keyLen+$ivLen){
            $key = Crypto::hashToLength($key, $keyLen+$ivLen);
        }
        $realKey = substr($key,0,$keyLen);
        $iv = substr($key,$keyLen,$ivLen);
        return array(
                'cipherType'=>$cipherType,
                'mode'=>$mode,
                'key'=>$realKey,
                'iv'=>$iv);
    }
    /**
     * 对称加密的加密（内部）
     @param string $data 数据
     @param string $key 密码
     */
    function _cipherEn($data,$key){
        $keys = $this->_cipherGetKey($key);
        extract($keys);
        $data = $this->dCipherEn($data,$cipherType,$mode,$key,$iv);
        return $data;
    }
    /**
     * 对称加密的解密（内部）
     @param string $data 数据
     @param string $key 密码
     */
    function _cipherDe($data,$key){
        $keys = $this->_cipherGetKey($key);
        extract($keys);
        $data = $this->dCipherDe($data,$cipherType,$mode,$key,$iv);
        return $data;
    }
    /**
     * hash密码（内部）
     @param string $data 输入密码
     @param string $salt 盐
     @param string $key 站点密码
     */
    function _hash($data,$salt,$key){
        $hashType = $this->config['hash'][0];
        $times =  $this->config['hash'][1];
        for($i=0;$i<$times;$i++){
            $data = $salt.$data.$key;
            $data = $this->dHash($data,$hashType);
        }
        return $data;
    }
    /**
     * 不对称加密的加密（未实现）（内部）
     @param string $data 数据
     @param string $key 密码
     */
    function _pkeyEn($data,$key=null){
        return tPanic('not implement',get_class($this));
    }
    /**
     * 不对称加密的解密（未实现）（内部）
     @param string $data 数据
     @param string $key 密码
     */
    function _pkeyDe($data,$key=null){
        return tPanic('not implement',get_class($this));
    }
    /**
     默认配置
     */
    static $_default = array(
            //对称加密，方法，分组方法
            'cipher'=>array('aes-256','cbc'),
            //hash哈希，方法，循环次数,储存密码盐长度
            'hash'=>array('sha512',500,10),
            //不对称加密，密码
            'pkey'=>array('rsa'),
            //对称加密站点密码
            'key'=>'CdrBxhEXBJia+yVoRgOrc72p4kf7NHRsAFzozJKvbya+BZcAqMRQCeR8wsnX7flf',
    );
    //模块接口转换
    /////////////////////////////////////////
    function dHash($data,$hashType){
        return tPanic('not implement',get_class($this));
    }
    function dCipherEn($data,$cipherType,$mode,$key,$iv){
        return tPanic('not implement',get_class($this));
    }
    function dCipherDe($data,$cipherType,$mode,$key,$iv){
        return tPanic('not implement',get_class($this));
    }
    function cipherKeySize($cipherType){
        return tPanic('not implement',get_class($this));
    }
    function cipherIvSize($cipherType){
        return tPanic('not implement',get_class($this));
    }
    function cipherBlockSize($cipherType){
        return tPanic('not implement',get_class($this));
    }
    function cipherPadEn($data,$cipherType){
        return tPanic('not implement',get_class($this));
    }
    function cipherPadDe($data,$cipherType){
        return tPanic('not implement',get_class($this));
    }
    //实例控制
    //////////////////////////////////////
    /**
    * 初始化
    @param array $config 配置
    */
    function init($config=null){
        if ($config!==null){
            $config = array_merge(self::$_default,$config);
        }else{
            $config = self::$_default;
        }
        $this->config = $config;
    }
    static $_cryptoObj = array();
    static $_cryptoObjCando = array();
    /**
     * 加载实现adpter
     @param string $className
     */
    static function loadClass($className){
        $className = self::$_classNamePrefix . $className;
        if (isset(self::$_cryptoObj[$className])){
            return self::$_cryptoObj[$className];
        }
        $can = $className::canLoad();
        if (!$can){
            self::$_cryptoObj[$className] = false;
        }else{
            self::$_cryptoObj[$className] = new $className(getConfig('crypto'));
            self::$_cryptoObj[$className]->init();
        }
        return self::$_cryptoObj[$className];
    }
    /**
     *
     * */
    static $_classNamePrefix = 'Tbf\Crypto\\';
    /**
     * adpter可以做的事情
     */
    static $_cryptoCando = array(
            'hash'=>array('OpensslCrypto','HashCrypto'),
            'cipher'=>array('McryptCrypto','OpensslCrypto'));
    /**
     * 根据需要做的事情加载实现类
     */
    static function loadCrypto($todo){
        if (isset(self::$_cryptoObjCando[$todo])){
            return self::$_cryptoObjCando[$todo];
        }
        $list = self::$_cryptoCando[$todo];
        foreach($list as $className){
            $obj = self::loadClass($className);
            if ($obj!==false){
                self::$_cryptoObjCando[$todo]=$obj;
                return $obj;
            }
        }
    }
    static function dObj(){
        static $cache = null;
        if ($cache===null){
            $cache = new static;
        }
        return $cache;
    } 
}

